package uk.ac.ebi.das.uniprot.image.controller;

import org.apache.log4j.Logger;
import org.json.JSONException;
import org.json.JSONObject;
import uk.ac.ebi.das.uniprot.image.exception.ImageException;
import uk.ac.ebi.das.uniprot.image.util.FeatureTypeConfigHelper;
import uk.ac.ebi.das.uniprot.image.util.RetrievalHelper;
import uk.ac.ebi.das.uniprot.image.util.Style;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.bind.JAXBException;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.HashMap;

/**
 * Created by IntelliJ IDEA.
 * User: ljgarcia
 * Date: 30-Mar-2011
 * Time: 15:46:20
 * To change this template use File | Settings | File Templates.
 */
public class ImageServlet extends HttpServlet {
    private static Logger logger = Logger.getLogger(ImageServlet.class);
    private static final String OPTION_IMAGE = "image";
    /**
	 * This method will ensure that all the plugins are registered and call
	 * the corresponding init() method on all of the plugins.
	 *
	 * Also initialises the XMLPullParser factory.
	 * @throws javax.servlet.ServletException
	 */
	public void init() throws ServletException {
		super.init();
	}

	/**
	 * This method will ensure that call the corresponding destroy() method on
	 * all of the registered plugins to allow them to clean up resources.
	 */
	public void destroy() {
        super.destroy();
	}

	/**
	 * Delegates to the parseAndHandleRequest method
	 * @param request containing details of the request, including the command and command arguments.
	 * @param response to which the HTTP header / XML response will be written
	 * @throws ServletException as defined in the HTTPServlet interface.
	 * @throws java.io.IOException as defined in the HTTPServlet interface.
	 */
	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		parseAndHandleRequest(request, response);
	}

	/**
	 * Delegates to the parseAndHandleRequest method
	 * @param request containing details of the request, including the command and command arguments.
	 * @param response to which the HTTP header / XML response will be written
	 * @throws ServletException as defined in the HTTPServlet interface.
	 * @throws java.io.IOException as defined in the HTTPServlet interface.
	 */
	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		parseAndHandleRequest(request, response);
	}

	/**
	 * Handles requests encoded as GET or POST.
	 * First of all splits up the request and then delegates to an appropriate method
	 * to respond to this request.  Only basic checking of the request is done here - checking of command
	 * arguments is the responsibility of the handling method.
	 *
	 * This method also handles all exceptions that can be reported as defined DAS errors and returns the
	 * appropriate X-DAS-STATUS HTTP header in the event of a problem.
	 * @param request The http request object.
	 * @param response The response - normally an XML file in HTTP/1.0 protocol.
	 * @throws ServletException in the event of an internal error
	 * @throws java.io.IOException in the event of a low level I/O error.
	 */
	private void parseAndHandleRequest(HttpServletRequest request, HttpServletResponse response)
	throws ServletException, IOException {
        this.parseImage(request, response);
    }

    private void parseImage(HttpServletRequest request, HttpServletResponse response)
	throws ServletException, IOException {
        String referenceURL = "";
        String tempSourceURLs = "";
        String sourceURLs;
        try {
            String segment = request.getParameter("segment"); //mandatory
            //No segment, just show the key with the default size
            String generatedImagesDir = RetrievalHelper.getGeneratedImagesDir();
            if ((segment == null) || (segment.length() == 0)) { //just show the key
                CartoonPainter painter = new ImagePainter(new Factory(), RetrievalHelper.getImageWidth()
                        , getServletContext().getRealPath(generatedImagesDir), false, false, false, Style.CENTERED.getStyle());
                painter.paintKey();
                response.sendRedirect(RetrievalHelper.getGeneratedImagesDir() + "/" + ImagePainter.KEY_FILE);
                return ;
            }

            int start, stop;
            try {
                start = Integer.parseInt(request.getParameter("start"));
                stop = Integer.parseInt(request.getParameter("stop"));
            } catch (Exception e) {
                start = stop = 0;
            }

            referenceURL = request.getParameter("dasReference");
            referenceURL = referenceURL == null || referenceURL.length() == 0 ? RetrievalHelper.getDasReferenceURL() : referenceURL;
            referenceURL = URLDecoder.decode(referenceURL, "UTF-8");

            tempSourceURLs = request.getParameter("dasSources");
            sourceURLs = tempSourceURLs == null || tempSourceURLs.length() == 0 ? RetrievalHelper.getDasSource() : tempSourceURLs;
            sourceURLs = URLDecoder.decode(sourceURLs, "UTF-8");

            String option = request.getParameter("option");
            option = option == null || option.length() == 0 ? RetrievalHelper.getOptionResponse() : option;

            int width = RetrievalHelper.getImageWidth();
            try {
                width = Integer.parseInt(request.getParameter("width"));
                width = width <= 0 ? RetrievalHelper.getImageWidth() : width;
            } catch (Exception e) {}

            String hgrid = request.getParameter("hgrid");
            boolean horizontalGrid = false;
            if (hgrid != null) {
                horizontalGrid = true;
            } else {
                horizontalGrid = false;
            }

            String vgrid = request.getParameter("vgrid");
            boolean verticalGrid = false;
            if (vgrid != null) {
                verticalGrid = true;
            } else {
                verticalGrid = false;
            }

            String style = request.getParameter("style");
            boolean nonOverlapping = false;
            style = style == null || style.length() == 0 ? RetrievalHelper.getImageStyle() : style;
            if (style.equals(Style.ROWS.getStyle())) {
                style = Style.ROWS.getStyle();
            } else if (style.equals(Style.NON_OVERLAPPING.getStyle())) {
                nonOverlapping = true;
            } else {//default
                style = Style.CENTERED.getStyle();
            }

            String allRectangle = request.getParameter("allRectangles");
            if (allRectangle != null) {
                AbstractPainter.ALL_RECTANGLE = true;
            } else {
                AbstractPainter.ALL_RECTANGLE = false;
            }

            String allFeatures = request.getParameter("allFeatures");
            if (allFeatures != null) {
                AbstractPainter.ALL_FEATURES = true;
            } else {
                AbstractPainter.ALL_FEATURES = false;
            }

            String allSameSize = request.getParameter("allSameSize");
            if (allSameSize != null) {
                AbstractPainter.CENTERED_SAME_SIZE= true;
            } else {
                AbstractPainter.CENTERED_SAME_SIZE = false;
            }

            HashMap<String, String> featureSourceMap = RetrievalHelper.getAllFeatureSource();
            Enumeration<String> parameters = request.getParameterNames();
            FeatureTypeConfigHelper.featureNameMap = new HashMap<String, String>();
            while (parameters.hasMoreElements()) {
                String parameter = parameters.nextElement();
                if (parameter.startsWith("feature.")) {
                    String value = request.getParameter(parameter);
                    String feature = parameter.substring("feature.".length()).toUpperCase();
                    featureSourceMap.put(feature, URLDecoder.decode(value, "UTF-8"));
                } else if (parameter.startsWith("name.")) {
                    String value = request.getParameter(parameter);
                    String feature = parameter.substring("name.".length()).toUpperCase();
                    //FeatureTypeConfig typeConfig = FeatureTypeConfigHelper.fetchByType(feature);
                    FeatureTypeConfigHelper.featureNameMap.put(feature, value);
                }
            }

            logger.debug("option: " + request.getParameter("option") + ", width: " + request.getParameter("width") +
                    ", das reference source: " + request.getParameter("dasReference") +
                    ", das annotation sources: " + request.getParameter("dasSources") +
                    ", segment: " + request.getParameter("segment") +
                    ", start: " + start + ", stop: " + stop +
                    ", style: " + style + ", horizontal grid: " + horizontalGrid + ", vertical grid: " + verticalGrid +
                    ", all rectangles: " + AbstractPainter.ALL_RECTANGLE + ", all features: " + AbstractPainter.ALL_FEATURES +
                    ", centered same size: " + AbstractPainter.CENTERED_SAME_SIZE +
                    ", features by source: " + featureSourceMap.toString() +
                    ", features modified names: " + FeatureTypeConfigHelper.featureNameMap.toString()
            );

            if (OPTION_IMAGE.equals(option)) {
                CartoonPainter painter = new ImagePainter(new Factory(), width, getServletContext().getRealPath(generatedImagesDir), horizontalGrid, verticalGrid, nonOverlapping, style);
                JSONObject resp = painter.paintSegment(referenceURL, sourceURLs, segment, featureSourceMap);
                try {
                    resp.get("error");
                    response.sendRedirect(generatedImagesDir + "/error.png");
                    return;
                } catch (JSONException e) { //no error was reported
                    String seg = resp.get("segment").toString();
                    response.sendRedirect(generatedImagesDir + "/" + seg + ".png");
                }
            } else {
                response.setContentType("application/json");
                CartoonPainter painter = new JsonPainter(new Factory(), width, horizontalGrid, verticalGrid, nonOverlapping, style);
                JSONObject obj = painter.paintSegment(referenceURL, sourceURLs, segment, featureSourceMap);
                response.getWriter().write(obj.toString());
            }
        } catch (MalformedURLException e) {
            logger.error("There is a problem with some of the URLs " + referenceURL + ", [" + tempSourceURLs + "] (" + e.getClass().getCanonicalName() + "): " + e.getMessage());
            e.printStackTrace();
            throw e;
        } catch (JAXBException e) {
            logger.error("The features response for some of the URLs " + referenceURL + "or [" + tempSourceURLs + "] cannot be parsed by JAXB (" + e.getClass().getCanonicalName() + "): " + e.getMessage());
            e.printStackTrace();
            throw new ServletException(e);
        } catch (JSONException e) {
            logger.error("The JSON objects for some of the URLs " + referenceURL + ", [" + tempSourceURLs + "] features cannot be created (" + e.getClass().getCanonicalName() + "): " + e.getMessage());
            e.printStackTrace();
            throw new ServletException(e);
        } catch (ImageException e) {
            logger.error("Generated images directory has not been configured, images cannot be saved (" + e.getClass().getCanonicalName() + "): " + e.getMessage());
            e.printStackTrace();
            throw new ServletException(e);
        } catch (Exception e) {
            logger.error("Unexpected exception (" + e.getClass().getCanonicalName() + "): " + e.getMessage());
            e.printStackTrace();
            throw new ServletException(e);
        }
    }

    /*
    private void parseJsonToSVGImage(HttpServletRequest request, HttpServletResponse response)
	throws ServletException, IOException {
        String json = request.getParameter("json");
        String protein = request.getParameter("protein");
        try {
            JSONArray array = new JSONArray(json);
            //Create SVG object
            String svg = "<?xml version='1.0' standalone='no'?>" + "\n";
            svg += "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\" " +
                    "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">" + "\n";
            svg += "<svg width='600' height='150' version='1.1' xmlns='http://www.w3.org/2000/svg'>" + "\n";
            //[{"type":"circle","cx":100,"cy":100,"r":30,"stroke":"red","fill-opacity":0.5,"fill":"blue"}]
            for (int i = 0; i < array.length(); i++){
                JSONObject obj = array.getJSONObject(i);
                if (obj.getString("type").equals("circle")) {
                    svg += "<circle" +
                        " cx='" + obj.getString("cx") + "'" +
                        " cy='" + obj.getString("cy") + "'" +
                        " r='" + obj.getString("r") + "'" +
                        " stroke='" + obj.getString("stroke") + "'" +
                        " fill-opacity='" + obj.getString("fill-opacity") + "'" +
                        " fill='" + obj.getString("fill") + "'" +
                    "/>" + "\n";
                }
            }
            svg += "</svg>";
            //Now convert into image and send response
            //Create a file protein.svg
            //Execute Batik
            String[] args = {"C:\\Users\\ljgarcia\\Desktop\\deleteme.svg"};//{protein + ".svg"};
            new Main(args).execute();
            response.getWriter().write(svg);
        } catch (JSONException e) {
            e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
        }
    }
    */
}
